package com.community.process._1010.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.community.process._1010.common.api.CommonResult;
import com.community.process._1010.common.api.ErrorAssert;
import com.community.process._1010.common.enums.OrderStatusEnum;
import com.community.process._1010.common.utils.*;
import com.community.process._1010.common.utils.redis.RedisUtil;
import com.community.process._1010.config.ProcessOrderDelayedQueueConfig;
import com.community.process._1010.config.property.ProcessProperty;
import com.community.process._1010.dao.entity.*;
import com.community.process._1010.dao.query.ProcessOrderQuery;
import com.community.process._1010.dao.query.ProcessTOrderQuery;
import com.community.process._1010.dao.service.*;
import com.community.process._1010.dao.view.ProcessOrderItemView;
import com.community.process._1010.dao.view.ProcessOrderView;
import com.community.process._1010.dao.view.ProcessTOrderView;
import com.community.process._1010.dao.vo.ProcessingCouponTypeVo;
import com.community.process._1010.domains.ProcessOrderDTO;
import com.community.process._1010.domains.ProcessOrderItemData;
import com.community.process._1010.domains.ProcessingCouponTypeDTO;
import com.community.process._1010.domains.external.CreateExternalOrderData;
import com.community.process._1010.event.CancelOrderEvent;
import com.community.process._1010.event.OvertimeOrderEvent;
import com.community.process._1010.event.RefundOrderEvent;
import com.community.process._1010.service.ProcessOrderService;
import com.community.process._1010.service.ProcessingCouponTypeService;
import com.oneadds.lowcode.dto.PageListData;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: Administrator to Zbs
 * @Date: 2024/9/6 星期五 13:09
 */
@Service
@RequiredArgsConstructor
public class ProcessOrderServiceImpl implements ProcessOrderService {

    private final ProcessingCouponTypeService processingCouponTypeService;
    private final ProcessIngredientsIService processIngredientsIService;
    private final ProcessFoodItemIService processFoodItemIService;
    private final ProcessTOrderIService processTOrderIService;
    private final ProcessOrderItemIService processOrderItemIService;
    private final ProcessOrderIService processOrderIService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcessTOrderView cartMinieOrder(ProcessOrderDTO.CartCreateOrder dto, Long userId, HttpServletRequest request) {
        // 获取订单项数据列表（从购物车中提取订单项数据）
        List<ProcessOrderItemData> processOrderItemDataList = getMiniOrderItemDataList(dto, userId);
        // 从订单项数据中提取 ProcessOrder 对象（每个订单项对应一个 ProcessOrder）
        List<ProcessOrder> processOrders = processOrderItemDataList.stream()
                .map(ProcessOrderItemData::getProcessOrder) // 提取 ProcessOrder 对象
                .collect(Collectors.toList()); // 收集成列表
        // 根据订单信息生成主订单（ProcessTOrder）
        ProcessTOrder processTOrder = generatedTMioiOrder(dto, userId, processOrders, dto.getContactPerson(), dto.getPhone(),dto.getShopId(),dto.getShopName(),dto.getDetailedAddress(),dto.getDinnerTime());
        // 保存主订单（将 ProcessTOrder 存储到数据库）
        processTOrderIService.save(processTOrder);
        // 创建一个列表来存储子订单视图（每个子订单会生成一个 ProcessOrderView）
        List<ProcessOrderView> subProcessOrders = new ArrayList<>();
        // 遍历每个订单项数据，生成相应的子订单及其商品
        processOrderItemDataList.forEach(itemData -> {
            ProcessOrder processOrder = itemData.getProcessOrder(); // 获取单个订单（ProcessOrder）
            // 设置子订单的 TId（将主订单的 ID 关联到每个子订单）
            processOrder.setShopId(dto.getShopId());
            processOrder.setDinnerTime(dto.getDinnerTime());
            processOrder.setShopName(dto.getShopName());
            processOrder.setDetailedAddress(dto.getDetailedAddress());
            processOrder.setTId(processTOrder.getId());
            // 保存子订单到数据库
            this.processOrderIService.save(processOrder);
            // 获取子订单的商品项，关联到对应的订单 ID
            List<ProcessOrderItem> collect = itemData.getOrdersItems().stream()
                    .peek(orderItem -> orderItem.setOrderId(processOrder.getId())) // 设置订单项的订单 ID
                    .collect(Collectors.toList()); // 收集所有订单项
            // 批量保存订单项到数据库
            processOrderItemIService.saveBatch(collect);
            // 创建子订单视图（ProcessOrderView），用于展示子订单信息
            ProcessOrderView processOrderView = new ProcessOrderView(processOrder);
            // 设置子订单视图的商品项列表
            processOrderView.setOrdersItems(collect.stream()
                    .map(ProcessOrderItemView::new) // 将订单项转化为订单项视图
                    .collect(Collectors.toList()));

            // 将子订单视图添加到列表中
            subProcessOrders.add(processOrderView);
        });
        // 调用外部接口创建实际订单（通过订单号和支付金额等信息）
        Long id = processTOrder.getId(); // 获取主订单的 ID
        CommonResult<CreateExternalOrderData> orderDataCommonResult = ExternalApiUtils.createOrder(
                StrUtil.format("dc{}", id), // 格式化外部订单号
                processTOrder.getPayAmount(), // 支付金额
                0L, // 代金券使用金额（默认值 0）
                dto.getCouponIds(), // 优惠券 ID 列表
                processTOrder, // 主订单信息
                subProcessOrders, // 子订单视图列表
                CommonUtils.getExternalTokenLoginDTO(request) // 获取外部请求所需的登录 Token
        );
        // 校验外部接口返回结果中的数据
        ErrorAssert.notNull(orderDataCommonResult.getData(), orderDataCommonResult.getMsg());
        CreateExternalOrderData createExternalOrderData = orderDataCommonResult.getData();
        // 计算实际支付金额（转换为分）
        Long amountActuallyPaid = new BigDecimal(createExternalOrderData.getPayAllAmount())
                .multiply(new BigDecimal(100)) // 转为分
                .longValue();
        // 计算优惠券金额（转换为分）
        Long couponAmount = new BigDecimal(createExternalOrderData.getCouponPrice())
                .multiply(new BigDecimal(100)) // 转为分
                .longValue();
        // 更新主订单的支付信息（包括实际支付金额和优惠券金额）
        this.processTOrderIService.updateById(new ProcessTOrder(id, createExternalOrderData.getOrderNo(), amountActuallyPaid, couponAmount));
        // 创建主订单视图对象，包含子订单信息
        ProcessTOrderView processTOrderView = new ProcessTOrderView(processTOrder);
        processTOrderView.setOrderList(subProcessOrders); // 设置子订单视图列表
        // 获取订单超时时间
        int orderTimeout = ProcessProperty.orderTimeout;
        // 将主订单信息以 JSON 格式存入 Redis，设置过期时间
        String jsonString = JSON.toJSONString(new ProcessTOrder(id, createExternalOrderData.getOrderNo()));
        RedisUtil.set(RedisConstant.getProcessOrderRedisKey(createExternalOrderData.getOrderNo()), jsonString, orderTimeout + 5 * 60);
        // 将消息发送到 RabbitMQ 延时队列，用于超时处理
        RabbitUtil.sendDelayedMessage(
                ProcessOrderDelayedQueueConfig.EXCHANGE_NAME,
                ProcessOrderDelayedQueueConfig.ROUTING_KEY,
                jsonString,
                orderTimeout // 延时处理时间
        );
        // 返回主订单视图对象
        return processTOrderView;
    }


    public List<ProcessOrderItemData> getMiniOrderItemDataList(ProcessOrderDTO.CartCreateOrder dto, Long userId) {
        // 获取购物车列表
        List<ProcessOrderDTO.Cart> carts = dto.getCarts();
        // 用于存储最终的订单项数据
        List<ProcessOrderItemData> orderItemData = new ArrayList<>();
        // 遍历每个购物车
        carts.forEach(cart -> {
            // 如果购物车中的商品列表不为空
            if (!CollectionUtils.isEmpty(cart.getItems())) {
                // 用于存储处理后的订单项列表
                List<ProcessOrderItem> ordersItems = new ArrayList<>();
                // 创建一个处理优惠券类型的 DTO 对象，用于查询优惠券类型列表
                ProcessingCouponTypeDTO.ProcessingCouponTypeSearch dtoMini = new ProcessingCouponTypeDTO.ProcessingCouponTypeSearch();
                // 调用服务获取所有的优惠券类型列表
                PageListData<ProcessingCouponTypeVo> processingCouponTypeList = processingCouponTypeService.getProcessingMioiCouponTypeList(dtoMini);
                // 从查询结果中获取优惠券类型的项
                List<ProcessingCouponTypeVo> items = processingCouponTypeList.getItems();
                // 创建一个 Map 来存储优惠券类型 ID 和对应的优惠券类型对象
                Map<Long, ProcessingCouponTypeVo> ingredientsMap = new HashMap<>();
                // 遍历所有的优惠券类型项，并将其 ID 和对象存入 Map
                for (ProcessingCouponTypeVo item : items) {
                    Long id = item.getId();
                    ingredientsMap.put(id, item);
                }
                // 遍历购物车中的每个商品
                cart.getItems().forEach(itemCart -> {
                    // 获取当前商品对应的优惠券类型对象（根据 itemId）
                    ProcessingCouponTypeVo ingredients = ingredientsMap.getOrDefault(itemCart.getItemId(), null);
                    // 如果找不到对应的优惠券类型，抛出异常
                    ErrorAssert.notNull(ingredients, "订单异常，未找到该菜品");
                    // 创建一个新的订单项，并加入到订单项列表
                    ProcessOrderItem orderItem = new ProcessOrderItem(cart, itemCart, ingredients);
                    ordersItems.add(orderItem);
                });
                // 创建一个新的处理订单对象，并初始化相关参数（如订单项名称、图片等）
                ProcessOrder processOrder = new ProcessOrder(dto, userId)
                        .initItemParam(cart, ordersItems.get(0).getItemName() + (ordersItems.size() > 1 ? "等" : ""), ordersItems.get(0).getImage());
                // 计算所有订单项的总金额
                long sum = ordersItems.stream().mapToLong(ProcessOrderItem::getTotalPrice).sum();
                System.out.println(sum);
                // 设置订单的支付金额
                processOrder.setPayAmount(sum);
                // 将订单项数据和订单对象封装为一个 ProcessOrderItemData 并加入到结果列表
                orderItemData.add(new ProcessOrderItemData(processOrder, ordersItems));
            }
        });
        // 返回所有的订单项数据
        return orderItemData;
    }



    private ProcessTOrder generatedTMioiOrder(ProcessOrderDTO.CartCreateOrder dto, Long userId, List<ProcessOrder> processOrders, String contactPerson, String phone,Long shopId,String shopName,String detailedAddress,String dinnerTime) {
        // 订单总金额
        Long payAmount = processOrders.stream().mapToLong(ProcessOrder::getPayAmount).sum();
        // 运费
        Long postAmount = 0L;
        // 优惠券金额
        Long couponAmount = 0L;

        ProcessTOrder tOrder = new ProcessTOrder();
        tOrder.setUserId(userId);
        tOrder.setItemName(processOrders.get(0).getItemName() + (processOrders.size() > 1 ? "等" : ""));
        tOrder.setItemImage(processOrders.get(0).getItemImage());
        tOrder.setOrderStatus(OrderStatusEnum.TO_BE_PAID.getCode());
        tOrder.setMainStatus(OrderStatusEnum.TO_BE_PAID.getMainStatus());
        tOrder.setPayAmount(payAmount);
        tOrder.setPostAmount(postAmount);
        tOrder.setCouponAmount(couponAmount);
        tOrder.setCouponIds(JSON.toJSONString(dto.getCouponIds()));
        tOrder.setContactPerson(contactPerson);
        tOrder.setPhone(phone);
        tOrder.setShopId(shopId);
        tOrder.setShopName(shopName);
        tOrder.setDinnerTime(dinnerTime);
        tOrder.setDetailedAddress(detailedAddress);
        return tOrder;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcessTOrderView createOrder(ProcessOrderDTO.CartCreateOrder dto, Long userId, HttpServletRequest request) {
        List<ProcessOrderItemData> processOrderItemDataList = getOrderItemDataList(dto, userId);

        List<ProcessOrder> processOrders = processOrderItemDataList.stream().map(ProcessOrderItemData::getProcessOrder).collect(Collectors.toList());
        ProcessTOrder processTOrder = generatedTOrder(dto, userId, processOrders, dto.getContactPerson(), dto.getPhone());
        // 创建主订单
        processTOrderIService.save(processTOrder);
        // 创建子订单
        List<ProcessOrderView> subProcessOrders = new ArrayList<>();
        processOrderItemDataList.forEach(itemData -> {
            ProcessOrder processOrder = itemData.getProcessOrder();
            processOrder.setTId(processTOrder.getId());
            this.processOrderIService.save(processOrder);

            // 保存子订单商品
            List<ProcessOrderItem> collect = itemData.getOrdersItems().stream()
                    .peek(orderItem -> orderItem.setOrderId(processOrder.getId()))
                    .collect(Collectors.toList());
            processOrderItemIService.saveBatch(collect);
            //  订单视图
            ProcessOrderView processOrderView = new ProcessOrderView(processOrder);
            processOrderView.setOrdersItems(collect.stream().map(ProcessOrderItemView::new).collect(Collectors.toList()));
            subProcessOrders.add(processOrderView);
        });

        // 接入主业务订单
        Long id = processTOrder.getId();
        CommonResult<CreateExternalOrderData> orderDataCommonResult = ExternalApiUtils.createOrder(StrUtil.format("dc{}", id), processTOrder.getPayAmount(), 0L, dto.getCouponIds(), processTOrder, subProcessOrders, CommonUtils.getExternalTokenLoginDTO(request));
        ErrorAssert.notNull(orderDataCommonResult.getData(), orderDataCommonResult.getMsg());
        CreateExternalOrderData createExternalOrderData = orderDataCommonResult.getData();
        // 实际支付金额
        Long amountActuallyPaid = new BigDecimal(createExternalOrderData.getPayAllAmount()).multiply(new BigDecimal(100)).longValue();
        Long couponAmount = new BigDecimal(createExternalOrderData.getCouponPrice()).multiply(new BigDecimal(100)).longValue();

        // 更新订单信息
        this.processTOrderIService.updateById(new ProcessTOrder(id, createExternalOrderData.getOrderNo(), amountActuallyPaid, couponAmount));

        ProcessTOrderView processTOrderView = new ProcessTOrderView(processTOrder);
        processTOrderView.setOrderList(subProcessOrders);

        int orderTimeout = ProcessProperty.orderTimeout;
        String jsonString = JSON.toJSONString(new ProcessTOrder(id, createExternalOrderData.getOrderNo()));
        RedisUtil.set(RedisConstant.getProcessOrderRedisKey(createExternalOrderData.getOrderNo()), jsonString, orderTimeout + 5 * 60);
        RabbitUtil.sendDelayedMessage(ProcessOrderDelayedQueueConfig.EXCHANGE_NAME, ProcessOrderDelayedQueueConfig.ROUTING_KEY, jsonString, orderTimeout);

        return processTOrderView;
    }

    public List<ProcessOrderItemData> getOrderItemDataList(ProcessOrderDTO.CartCreateOrder dto, Long userId) {
        List<ProcessOrderDTO.Cart> carts = dto.getCarts();
        List<ProcessOrderItemData> orderItemData = new ArrayList<>();
        carts.forEach(cart -> {
            if (!CollectionUtils.isEmpty(cart.getItems())) {
                List<Long> itemIds = cart.getItems().stream().map(ProcessOrderDTO.ItemCart::getItemId).collect(Collectors.toList());
                List<ProcessOrderItem> ordersItems = new ArrayList<>();
                // 1 自带食材  3 自组食材
                if (cart.getType().equals(1) || cart.getType().equals(3)) {
                    // 商品食材map
                    Map<Long, ProcessIngredients> ingredientsMap = processIngredientsIService.findByIdInToMap(itemIds);
                    cart.getItems().forEach(itemCart -> {
                        ProcessIngredients ingredients = ingredientsMap.getOrDefault(itemCart.getItemId(), null);
                        ErrorAssert.notNull(ingredients, "订单异常，未找到食材");
                        ProcessOrderItem orderItem = new ProcessOrderItem(cart, itemCart, ingredients);
                        ordersItems.add(orderItem);
                    });
                }
                // 2 成品菜
                if (cart.getType().equals(2)) {
                    // 成品菜商品map
                    Map<Long, ProcessFoodItem> foodItemMap = processFoodItemIService.findByIdsInToMap(itemIds);
                    cart.getItems().forEach(itemCart -> {
                        ProcessFoodItem foodItem = foodItemMap.getOrDefault(itemCart.getItemId(), null);
                        ErrorAssert.notNull(foodItem, "订单异常，未找到菜品");
                        ProcessOrderItem orderItem = new ProcessOrderItem(cart, itemCart, foodItem);
                        ordersItems.add(orderItem);
                    });
                }
                ProcessOrder processOrder = new ProcessOrder(dto, userId).initItemParam(cart, ordersItems.get(0).getItemName() + (ordersItems.size() > 1 ? "等" : ""), ordersItems.get(0).getImage());
                long sum = ordersItems.stream().mapToLong(ProcessOrderItem::getTotalPrice).sum();
                processOrder.setPayAmount(sum);
                orderItemData.add(new ProcessOrderItemData(processOrder, ordersItems));
            }
        });
        return orderItemData;
    }

    private ProcessTOrder generatedTOrder(ProcessOrderDTO.CartCreateOrder dto, Long userId, List<ProcessOrder> processOrders, String contactPerson, String phone) {
        // 订单总金额
        Long payAmount = processOrders.stream().mapToLong(ProcessOrder::getPayAmount).sum();
        // 运费
        Long postAmount = 0L;
        // 优惠券金额
        Long couponAmount = 0L;

        ProcessTOrder tOrder = new ProcessTOrder();
        tOrder.setUserId(userId);
        tOrder.setItemName(processOrders.get(0).getItemName() + (processOrders.size() > 1 ? "等" : ""));
        tOrder.setItemImage(processOrders.get(0).getItemImage());
        tOrder.setOrderStatus(OrderStatusEnum.TO_BE_PAID.getCode());
        tOrder.setMainStatus(OrderStatusEnum.TO_BE_PAID.getMainStatus());
        tOrder.setPayAmount(payAmount);
        tOrder.setPostAmount(postAmount);
        tOrder.setCouponAmount(couponAmount);
        tOrder.setCouponIds(JSON.toJSONString(dto.getCouponIds()));
        tOrder.setContactPerson(contactPerson);
        tOrder.setPhone(phone);
        return tOrder;
    }

    @Override
    public PageListData<ProcessTOrderView> queryTOrder(ProcessTOrderQuery queryVo, Long userId) {
        if (userId != null) {
            queryVo.setUserId(userId);
        }
        Page<ProcessTOrder> page = processTOrderIService.lambdaQuery()
                .eq(StringUtils.isNotBlank(queryVo.getTradeNo()), ProcessTOrder::getTradeNo, queryVo.getTradeNo())
                .eq(Objects.nonNull(queryVo.getOrderStatus()), ProcessTOrder::getOrderStatus, queryVo.getOrderStatus())
                .eq(Objects.nonNull(queryVo.getOrderType()), ProcessTOrder::getOrderType, queryVo.getOrderType())
                .eq(StringUtils.isNotBlank(queryVo.getItemName()), ProcessTOrder::getItemName, queryVo.getItemName())
                .eq(Objects.nonNull(queryVo.getUserId()), ProcessTOrder::getUserId, queryVo.getUserId())
                .orderByDesc(ProcessTOrder::getCreateTime)
                .page(PageUtil.page(queryVo));
        List<ProcessTOrderView> orderViews = page.getRecords().stream().map(ProcessTOrderView::new).collect(Collectors.toList());
        return new PageListData<>(page.getSize(), page.getTotal(), orderViews);
    }

    @Override
    public PageListData<ProcessOrderView> queryCOrder(ProcessOrderQuery queryVo, Long userId) {
        if (userId != null) {
            queryVo.setUserId(userId);
        }
        Page<ProcessOrder> page = processOrderIService.lambdaQuery().eq(Objects.nonNull(queryVo.getOrderStatus()), ProcessOrder::getOrderStatus, queryVo.getOrderStatus())
                .eq(Objects.nonNull(queryVo.getOrderType()), ProcessOrder::getOrderType, queryVo.getOrderType())
                .eq(StringUtils.isNotBlank(queryVo.getItemName()), ProcessOrder::getItemName, queryVo.getItemName())
                .eq(Objects.nonNull(queryVo.getTId()), ProcessOrder::getTId, queryVo.getTId())
                .eq(Objects.nonNull(queryVo.getUserId()), ProcessOrder::getUserId, queryVo.getUserId())
                .orderByDesc(ProcessOrder::getCreateTime)
                .page(PageUtil.page(queryVo));
        List<ProcessOrderView> orderViewList = page.getRecords().stream().map(ProcessOrderView::new).collect(Collectors.toList());
        return new PageListData<>(page.getSize(), page.getTotal(), orderViewList);
    }

    @Override
    public ProcessOrderView queryCOrderDetail(Long id) {
        ProcessOrder processOrder = this.processOrderIService.getById(id);
        List<ProcessOrderItemView> item = this.processOrderItemIService.lambdaQuery()
                .eq(ProcessOrderItem::getOrderId, id)
                .list()
                .stream().map(ProcessOrderItemView::new).collect(Collectors.toList());
        ProcessOrderView processOrderView = new ProcessOrderView(processOrder);
        processOrderView.setOrdersItems(item);
        return processOrderView;
    }

    @Override
    public List<ProcessOrderView> queryCOrderList(Long tid) {
        List<ProcessOrder> orderList = processOrderIService.lambdaQuery()
                .eq(ProcessOrder::getTId, tid)
                .list();
        List<Long> orderIds = orderList.stream().map(ProcessOrder::getId).collect(Collectors.toList());
        Map<Long, List<ProcessOrderItem>> orderItemsMap = processOrderItemIService.lambdaQuery().in(ProcessOrderItem::getOrderId, orderIds).list().stream().collect(Collectors.groupingBy(ProcessOrderItem::getOrderId));
        return orderList
                .stream().map(processOrder -> {
                    ProcessOrderView processOrderView = new ProcessOrderView(processOrder);
                    List<ProcessOrderItem> orderItems = orderItemsMap.getOrDefault(processOrder.getId(), null);
                    if (orderItems != null) {
                        processOrderView.setOrdersItems(orderItems.stream().map(ProcessOrderItemView::new).collect(Collectors.toList()));
                    }
                    return processOrderView;
                })
                .collect(Collectors.toList());
    }

    @Override
    public ProcessTOrderView queryTOrderDetail(Long id) {
        ProcessTOrder tOrder = this.processTOrderIService.getById(id);
        ProcessTOrderView processTOrderView = new ProcessTOrderView(tOrder);
        processTOrderView.setOrderList(this.queryCOrderList(id));
        return processTOrderView;
    }

    @Override
    public void cancelAnOrder(Long id, Long userId, HttpServletRequest request) {
        // 取消订单逻辑
        ProcessTOrder order = this.processTOrderIService.getById(id);
        ErrorAssert.notNull(order, "订单不存在");

        ProcessTOrder updateOrder = new ProcessTOrder();
        updateOrder.setId(id);
        if (order.getUserId().equals(userId)) {
            // 买家取消订单 ps: 买家关闭订单只能待支付的情况才能关闭订单，否则只能走申请退款
            updateOrder.setOrderStatus(OrderStatusEnum.USER_CLOSE.getCode());
            updateOrder.setMainStatus(OrderStatusEnum.USER_CLOSE.getMainStatus());
            // 买家订单逻辑
            CompletableFuture.runAsync(() -> {
                SpringUtil.getApplicationContext().publishEvent(new CancelOrderEvent(CommonUtils.getExternalTokenLoginDTO(request), order));
            });
        } else {
            if (!Objects.equals(order.getOrderStatus(), OrderStatusEnum.TO_BE_PAID.getCode())) {
                // 商家发起退款
                CompletableFuture.runAsync(() -> {
                    SpringUtil.getApplicationContext().publishEvent(new RefundOrderEvent(CommonUtils.getExternalTokenLoginDTO(request), order));
                });
            } else {
                // 商家取消订单
                CompletableFuture.runAsync(() -> {
                    SpringUtil.getApplicationContext().publishEvent(new CancelOrderEvent(CommonUtils.getExternalTokenLoginDTO(request), order));
                });
            }
            // 商家取消订单
            updateOrder.setOrderStatus(OrderStatusEnum.SELLER_CLOSE.getCode());
            updateOrder.setMainStatus(OrderStatusEnum.SELLER_CLOSE.getMainStatus());
        }
        this.processTOrderIService.updateById(updateOrder);
        this.processOrderIService.lambdaUpdate()
                .set(ProcessOrder::getOrderStatus, updateOrder.getOrderStatus())
                .eq(ProcessOrder::getTId, id)
                .update();
    }

    @Override
    public void cancelAnOrder(Long tId, OrderStatusEnum orderStatusEnum) {
        ProcessTOrder tOrder = this.processTOrderIService.getById(tId);
        // 主业务设置超时订单
        CompletableFuture.runAsync(() -> {
            SpringUtil.getApplicationContext().publishEvent(new OvertimeOrderEvent(tOrder.getTradeNo()));
        });
        // 设置订单超时
        this.setOrderStatus(tId, orderStatusEnum);
        // 删除缓存
        RedisUtil.del(RedisConstant.getProcessOrderRedisKey(tOrder.getTradeNo()));
    }

    private void setOrderStatus(Long tId, OrderStatusEnum orderStatusEnum) {
        ProcessTOrder updateOrder = new ProcessTOrder();
        updateOrder.setId(tId);
        // 设置订单状态
        updateOrder.setOrderStatus(orderStatusEnum.getCode());
        updateOrder.setMainStatus(orderStatusEnum.getMainStatus());
        this.processTOrderIService.updateById(updateOrder);
        this.processOrderIService.lambdaUpdate()
                .set(ProcessOrder::getOrderStatus, updateOrder.getOrderStatus())
                .eq(ProcessOrder::getTId, tId)
                .update();
    }

    @Override
    public void applyForARefund(ProcessOrderDTO.ApplyForARefundDTO dto, Long userId) {
        ErrorAssert.notNull(dto.getId(), "订单ID不能为空");
        // 申请退款逻辑
        ProcessTOrder order = this.processTOrderIService.getById(dto.getId());
        ErrorAssert.notNull(order, "订单不存在");
        // 申请退款是买家操作的，判断当前登录用户是不是买家的ID
        ErrorAssert.isTrue(order.getUserId().equals(userId), "非法操作");
        ProcessTOrder updateOrder = new ProcessTOrder();
        updateOrder.setId(dto.getId());
        // 修改为申请退款状态
        updateOrder.setOrderStatus(OrderStatusEnum.APPLYING_FOR_A_REFUND.getCode());
        if (org.springframework.util.StringUtils.hasLength(dto.getRefundReason())) {
            // TODO 如果前端有传退款原因，直接写入即可
            updateOrder.setRefundReason(dto.getRefundReason());
        }
        this.processTOrderIService.updateById(updateOrder);
        this.processOrderIService.lambdaUpdate()
                .set(ProcessOrder::getOrderStatus, updateOrder.getOrderStatus())
                .eq(ProcessOrder::getTId, dto.getId())
                .update();
    }

    @Override
    public void agreeToRefund(Long id, Long userId, HttpServletRequest request) {
        // 同意退款逻辑
        ProcessTOrder order = this.processTOrderIService.getById(id);
        ErrorAssert.notNull(order, "订单不存在");
        // 同意退款是由商家发货的，判断当前订单是不是商家发起的
        ProcessTOrder updateOrder = new ProcessTOrder();
        updateOrder.setId(id);
        // 修改为退款完结状态
        updateOrder.setOrderStatus(OrderStatusEnum.REFUND_COMPLETED.getCode());
        // 走退款逻辑 商家同意退款
        CompletableFuture.runAsync(() -> {
            SpringUtil.getApplicationContext().publishEvent(new RefundOrderEvent(CommonUtils.getExternalTokenLoginDTO(request), order));
        });
        this.processTOrderIService.updateById(updateOrder);
        this.processOrderIService.lambdaUpdate()
                .set(ProcessOrder::getOrderStatus, updateOrder.getOrderStatus())
                .eq(ProcessOrder::getTId, order.getId())
                .update();
    }

    @Override
    public void refusedToRefund(ProcessOrderDTO.RefundForARefundDTO dto, Long userId) {
        ErrorAssert.notNull(dto.getId(), "订单ID不能为空");
        // 申请退款逻辑
        ProcessTOrder order = this.processTOrderIService.getById(dto.getId());
        ErrorAssert.notNull(order, "订单不存在");
        ProcessTOrder updateOrder = new ProcessTOrder();
        ErrorAssert.isTrue(order.getOrderStatus().equals(OrderStatusEnum.APPLYING_FOR_A_REFUND.getCode()), "非申请退款状态，不可操作");
        updateOrder.setId(dto.getId());
        // 修改为申请退款状态
        updateOrder.setOrderStatus(OrderStatusEnum.HAVE_PAID.getCode());
        updateOrder.setReason(dto.getReason());
        this.processTOrderIService.updateById(updateOrder);
        this.processOrderIService.lambdaUpdate()
                .set(ProcessOrder::getOrderStatus, updateOrder.getOrderStatus())
                .eq(ProcessOrder::getTId, order.getId())
                .update();
    }
}